package com.wmobile.utils.comm;

import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.kobjects.base64.Base64;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import com.wmobile.utils.BaseObject;
import com.wmobile.utils.CommUtils;
import com.wmobile.utils.ServiceResponse;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class Webservice extends CommUtils {
	private String modelPackageLocation = "com.wellsoft.mobileoa.wsmodel.";
	private SimpleDateFormat wsStdSdf = new SimpleDateFormat(
			"yyyy-MM-dd'T'HH:mm:ss");
	private String NameSpace;
	boolean IsDotNet = false;
	private final String RESPONSECOOKIE = "set-cookie";
	private final String REQUESTCOOKIE = "cookie";
	private List requestHeaders = new ArrayList();
	private List responseHeaders = null;
	private int timeOutLength = 30000;
	private Handler mHandler = null;
	private ServiceResponse wsResponse = null;

	public Webservice(Object... params) {
		this.WebserviceUrl = params[0].toString();
		this.NameSpace = params[1].toString();
		if (params[2].toString().equals("true")) {
			this.IsDotNet = true;
		} else {
			this.IsDotNet = false;
		}
	}

	/**
	 * 调用服务
	 */
	@Override
	public ServiceResponse CallserviceOnMainThread(
			final Map<String, BaseObject> requests, final String action,
			final Class<?> returnClass) {
		// 封装SoapObject
		SoapObject soapObject = GenerateSoapObject(action, requests, NameSpace);

		// 封装SoapSerializationEnvelope
		SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(
				SoapEnvelope.VER11);

		soapEnvelope.implicitTypes = true;
		soapEnvelope.setAddAdornments(false);
		soapEnvelope.dotNet = IsDotNet;
		soapEnvelope.bodyOut = soapObject;
		soapEnvelope.setOutputSoapObject(soapObject);

		// 封装HttpTransportSE
		HttpTransportSE httpTransportSE = new HttpTransportSE(WebserviceUrl,
				timeOutLength);
		httpTransportSE.debug = true;
		try {
			// 调用Webservice
			responseHeaders = httpTransportSE.call(NameSpace + action,
					soapEnvelope, requestHeaders);
			for (int ix = 0; ix < responseHeaders.size(); ix++) {
				HeaderProperty hp = (HeaderProperty) responseHeaders.get(ix);
				if (hp.getKey() != null && hp.getKey().equals(RESPONSECOOKIE)) {
					requestHeaders.clear();
					requestHeaders.add(new HeaderProperty(REQUESTCOOKIE, hp
							.getValue()));
				}
			}
			SoapObject soapResponse = (SoapObject) soapEnvelope.bodyIn;
			Log.v("Webservice", httpTransportSE.requestDump);
			Log.v("Webservice", httpTransportSE.responseDump);

			// 将Webservice返回结果转为所需的对象
			return new ServiceResponse(true, ParseSoapObject(
					soapResponse.getProperty(0), returnClass));
		} catch (Exception e) {
			e.printStackTrace();
			return new ServiceResponse(false, "无法连接到服务器，请检查网络及服务器地址！");
		}
	}

//	/**
//	 * 异步调用服务
//	 */
//	@Override
//	public ServiceResponse Callservice(final Map<String, BaseObject> requests,
//			final String action, final Class<?> returnClass) {
//		// 封装SoapObject
//		SoapObject soapObject = GenerateSoapObject(action, requests, NameSpace);
//
//		// 封装SoapSerializationEnvelope
//		SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(
//				SoapEnvelope.VER11);
//
//		soapEnvelope.implicitTypes = true;
//		soapEnvelope.setAddAdornments(false);
//		soapEnvelope.dotNet = IsDotNet;
//		soapEnvelope.bodyOut = soapObject;
//		soapEnvelope.setOutputSoapObject(soapObject);
//
//		// 封装HttpTransportSE
//		HttpTransportSE httpTransportSE = new HttpTransportSE(WebserviceUrl,
//				timeOutLength);
//		httpTransportSE.debug = true;
//		try {
//			// 调用Webservice
//			responseHeaders = httpTransportSE.call(NameSpace + action,
//					soapEnvelope, requestHeaders);
//			for (int ix = 0; ix < responseHeaders.size(); ix++) {
//				HeaderProperty hp = (HeaderProperty) responseHeaders.get(ix);
//				if (hp.getKey() != null && hp.getKey().equals(RESPONSECOOKIE)) {
//					requestHeaders.clear();
//					requestHeaders.add(new HeaderProperty(REQUESTCOOKIE, hp
//							.getValue()));
//				}
//			}
//			SoapObject soapResponse = (SoapObject) soapEnvelope.bodyIn;
//			Log.v("Webservice", httpTransportSE.requestDump);
//			Log.v("Webservice", httpTransportSE.responseDump);
//
//			// 将Webservice返回结果转为所需的对象
//			return new ServiceResponse(true, ParseSoapObject(
//					soapResponse.getProperty(0), returnClass));
//		} catch (Exception e) {
//			e.printStackTrace();
//			return new ServiceResponse(false, "无法连接到服务器，请检查网络及服务器地址！");
//		}
//	}
	

	/**
	 * 异步调用服务
	 */
	@Override
	public ServiceResponse Callservice(final Map<String, BaseObject> requests,
			final String action, final Class<?> returnClass) {
		mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				wsResponse = (ServiceResponse) msg.obj;
				this.getLooper().quit();
			}
		};

		new Thread(new Runnable() {
			public void run() {
				// 封装SoapObject
				SoapObject soapObject = GenerateSoapObject(action, requests,
						NameSpace);

				// 封装SoapSerializationEnvelope
				SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(
						SoapEnvelope.VER11);

				soapEnvelope.implicitTypes = true;
				soapEnvelope.setAddAdornments(false);
				soapEnvelope.dotNet = IsDotNet;
				soapEnvelope.bodyOut = soapObject;
				soapEnvelope.setOutputSoapObject(soapObject);

				// 封装HttpTransportSE
				HttpTransportSE httpTransportSE = new HttpTransportSE(
						WebserviceUrl, timeOutLength);
				httpTransportSE.debug = true;
				try {
					// 调用Webservice
					responseHeaders = httpTransportSE.call(NameSpace + action,
							soapEnvelope, requestHeaders);
					for (int ix = 0; ix < responseHeaders.size(); ix++) {
						HeaderProperty hp = (HeaderProperty) responseHeaders
								.get(ix);
						if (hp.getKey() != null
								&& hp.getKey().equals(RESPONSECOOKIE)) {
							requestHeaders.clear();
							requestHeaders.add(new HeaderProperty(
									REQUESTCOOKIE, hp.getValue()));
						}
					}
					SoapObject soapResponse = (SoapObject) soapEnvelope.bodyIn;
					Log.v("Webservice", httpTransportSE.requestDump);
					Log.v("Webservice", httpTransportSE.responseDump);

					// 将Webservice返回结果转为所需的对象
					ServiceResponse response = new ServiceResponse(true,
							ParseSoapObject(soapResponse.getProperty(0),
									returnClass));
					Message msg = mHandler.obtainMessage();
					msg.obj = response;
					mHandler.sendMessage(msg);
				} catch (Exception e) {
					e.printStackTrace();
					// ServiceResponse response = new ServiceResponse(false,
					// "网络错误：" + e.toString() + " returnXml = "
					// + httpTransportSE.responseDump);
					ServiceResponse response = new ServiceResponse(false,
							"无法连接到服务器，请检查网络及服务器地址！");
					Message msg = mHandler.obtainMessage();
					msg.obj = response;
					mHandler.sendMessage(msg);
				}
			}
		}).start();
		try {
			Looper.getMainLooper().loop();
		} catch (Exception e2) {
			e2.printStackTrace();
			if (wsResponse == null) {
				wsResponse = new ServiceResponse(false);
			}
		}
		return wsResponse;
	}

	/**
	 * 转换通讯对象到SoapObject
	 */
	public SoapObject GenerateSoapObject(String action,
			Map<String, BaseObject> requests, String NameSpace) {
		SoapObject soapObject = new SoapObject(NameSpace, action);

		Iterator<Map.Entry<String, BaseObject>> iter = requests.entrySet()
				.iterator();
		while (iter.hasNext()) {
			Map.Entry entry = (Map.Entry) iter.next();

			String paramName = (String) entry.getKey();
			BaseObject paramValue = (BaseObject) entry.getValue();

			soapObject
					.addProperty(paramName, SerializeToSoapObject(paramValue));
		}
		return soapObject;
	}

	/**
	 * 转换返回的SoapObject
	 */
	public BaseObject ParseSoapObject(Object soapObj, Class<?> targetClass) {
		if (soapObj == null)
			return null;
		SoapObject soapObject = (SoapObject) soapObj;
		BaseObject result = null;
		try {
			result = (BaseObject) targetClass.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		for (Field fd : targetClass.getDeclaredFields()) {
			fd.setAccessible(true);
			Object fieldValue;
			if (!soapObject.hasProperty(fd.getName())) {
				continue;
			}
			fieldValue = soapObject.getProperty(fd.getName());
			Class<?> fdCls = fd.getType();
			try {
				if (fdCls.isPrimitive()) {
					if (fdCls.getName().equals("int")) {
						fd.setInt(result,
								Integer.parseInt(fieldValue.toString()));
					} else if (fdCls.getName().equals("long")) {
						fd.setLong(result,
								Long.parseLong(fieldValue.toString()));
					} else if (fdCls.getName().equals("float")) {
						fd.setFloat(result,
								Float.parseFloat(fieldValue.toString()));
					} else if (fdCls.getName().equals("double")) {
						fd.setDouble(result,
								Double.parseDouble(fieldValue.toString()));
					}
				} else if (fdCls.getName().equals("java.lang.String")) {
					if (fieldValue.toString().equals("anyType{}")) {
						fd.set(result, "");
					} else {
						fd.set(result, fieldValue.toString());
					}
				} else if (fdCls.getName().equals("java.util.Date")) {
					fd.set(result, wsStdSdf.parse(fieldValue.toString()));

				} else if (fdCls.getName().equals("java.util.List")) {
					List<Object> listObjects = new ArrayList<Object>();
					SoapObject subSoapObject = (SoapObject) fieldValue;
					for (int i = 0; i < subSoapObject.getPropertyCount(); i++) {
						PropertyInfo pi = new PropertyInfo();
						subSoapObject.getPropertyInfo(i, pi);
						if (pi.getName().equals("string")) {
							if (subSoapObject.getProperty(i).toString()
									.equals("anyType{}")) {
								listObjects.add("");
							} else {
								listObjects.add(subSoapObject.getProperty(i)
										.toString());
							}
						} else {
							listObjects.add(ParseSoapObject(
									subSoapObject.getProperty(i),
									Class.forName(modelPackageLocation
											+ pi.getName())));
						}
						pi = null;
					}
					fd.set(result, listObjects);
				} else if (fdCls.isArray()) {
					if (fdCls.getName().equals("[B")) {
						Object obj = Base64.decode(fieldValue.toString());
						fd.set(result, obj);
					} else {
						SoapObject subSoapObject = (SoapObject) fieldValue;
						Object[] arrayObjects = new Object[subSoapObject
								.getPropertyCount()];
						for (int i = 0; i < subSoapObject.getPropertyCount(); i++) {
							arrayObjects[i] = ParseSoapObject(
									subSoapObject.getProperty(i), fdCls);
						}
						fd.set(result, arrayObjects);
					}
				} else if (fdCls.isSynthetic()) {

				} else if (fdCls.isInterface()) {

				} else {
					fd.set(result, ParseSoapObject(fieldValue, fdCls));
				}
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (ParseException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			fd.setAccessible(false);
		}
		return result;
	}

	/**
	 * 将对象转换至SoapObject
	 */
	public SoapObject SerializeToSoapObject(BaseObject obj) {
		if (obj == null)
			return null;
		SoapObject soapObject = new SoapObject(obj.getNameSpace(), obj
				.getClass().getSimpleName());
		soapObject.addAttribute("xmlns", obj.getNameSpace());
		Class<? extends BaseObject> cls = obj.getClass();
		Field[] fields = cls.getDeclaredFields();
		for (Field fd : fields) {
			fd.setAccessible(true);
			Object fieldValue;
			try {
				fieldValue = fd.get(obj);
				if (fieldValue == null) {
					soapObject.addProperty(fd.getName(), null);
					continue;
				}
			} catch (IllegalArgumentException e1) {
				// TODO Auto-generated catch block
				soapObject.addProperty(fd.getName(), null);
				e1.printStackTrace();
				continue;
			} catch (IllegalAccessException e1) {
				// TODO Auto-generated catch block
				soapObject.addProperty(fd.getName(), null);
				e1.printStackTrace();
				continue;
			}
			Class<?> fdCls = fd.getType();
			if (fdCls.isPrimitive()) {
				if (fdCls.getName().equals("int")) {
					soapObject.addProperty(fd.getName(), fieldValue.toString());
				} else if (fdCls.getName().equals("long")) {
					soapObject.addProperty(fd.getName(), fieldValue.toString());
				} else if (fdCls.getName().equals("float")) {
					soapObject.addProperty(fd.getName(), fieldValue.toString());
				} else if (fdCls.getName().equals("double")) {
					soapObject.addProperty(fd.getName(), fieldValue.toString());
				}
			} else if (fdCls.getName().equals("java.util.Date")) {
				soapObject.addProperty(fd.getName(),
						wsStdSdf.format((Date) fieldValue));
			} else if (fdCls.getName().equals("java.lang.String")) {
				soapObject.addProperty(fd.getName(), fieldValue.toString());
			} else if (fdCls.getName().equals("java.util.List")) {
				List<Object> objs = null;
				objs = (List<Object>) fieldValue;
				if (objs == null || objs.size() == 0 || objs.get(0) == null) {
					soapObject.addProperty(fd.getName(), null);
					continue;
				}
				SoapObject subSoapObject = null;

				if (objs.get(0).getClass().isPrimitive()) {
					if (fdCls.getName().equals("int")) {
						subSoapObject = new SoapObject("", "int[]");
						for (Object subObj : objs) {
							subSoapObject.addProperty("int", subObj.toString());
						}
					} else if (objs.get(0).getClass().getName().equals("long")) {
						subSoapObject = new SoapObject("", "int[]");
						for (Object subObj : objs) {
							subSoapObject.addProperty("int", subObj.toString());
						}
					} else if (objs.get(0).getClass().getName().equals("float")) {
						subSoapObject = new SoapObject("", "double[]");
						for (Object subObj : objs) {
							subSoapObject.addProperty("double",
									subObj.toString());
						}
					} else if (objs.get(0).getClass().getName()
							.equals("double")) {
						subSoapObject = new SoapObject("", "double[]");
						for (Object subObj : objs) {
							subSoapObject.addProperty("double",
									subObj.toString());
						}
					}
				} else if (objs.get(0).getClass().getName()
						.equals("java.util.Date")) {
					subSoapObject = new SoapObject("", "string[]");
					for (Object subObj : objs) {
						subSoapObject.addProperty("string",
								wsStdSdf.format((Date) subObj));
					}
				} else if (objs.get(0).getClass().getName()
						.equals("java.lang.String")) {
					subSoapObject = new SoapObject("", null);
					for (Object subObj : objs) {
						subSoapObject.addProperty("string", subObj.toString());
					}
				} else if (objs.get(0).getClass().getName()
						.equals("java.util.List")) {
				} else if (objs.get(0).getClass().isArray()) {

				} else {
					subSoapObject = new SoapObject(
							((BaseObject) objs.get(0)).getNameSpace(), objs
									.get(0).getClass().getSimpleName()
									+ "[]");
					subSoapObject.addAttribute("xmlns",
							((BaseObject) objs.get(0)).getNameSpace());
					for (Object subObj : objs) {
						subSoapObject.addProperty(subObj.getClass()
								.getSimpleName(),
								SerializeToSoapObject((BaseObject) subObj));
					}
				}
				soapObject.addProperty(fd.getName(), subSoapObject);
			} else if (fdCls.isArray()) {
				BaseObject[] objs = null;
				objs = (BaseObject[]) fieldValue;
				if (objs == null || objs.length == 0 || objs[0] == null) {
					soapObject.addProperty(fd.getName(), null);
					continue;
				}
				SoapObject subSoapObject = new SoapObject(
						objs[0].getNameSpace(), objs[0].getClass()
								.getSimpleName() + "[]");
				subSoapObject.addAttribute("xmlns", objs[0].getNameSpace());
				for (BaseObject subObj : objs) {
					subSoapObject.addProperty(
							subObj.getClass().getSimpleName(),
							SerializeToSoapObject(subObj));
				}
				soapObject.addProperty(fd.getName(), subSoapObject);
			} else if (fdCls.isSynthetic()) {
			} else if (fdCls.isInterface()) {
			} else {
				soapObject.addProperty(fd.getName(),
						SerializeToSoapObject((BaseObject) fieldValue));
			}
			fd.setAccessible(false);
		}
		return soapObject;
	}
}